package org.f0rb.demo.model.user;

import org.f0rb.core.event.EventManager;
import org.f0rb.core.security.SecurityWebServiceImpl;
import org.f0rb.core.web.WebServiceResult;
import org.f0rb.demo.common.Message;
import org.f0rb.demo.utils.SecureUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;

import static org.f0rb.demo.web.Context.*;

/**
 * UserServiceImpl.
 *
 * @author f0rb
 * @version 1.0.0 2010-1-27
 */
public final class UserServiceImpl extends SecurityWebServiceImpl<User> {
    private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);

    @Resource
    private UserDAO userDAO;

    @Resource
    private EventManager<UserEvent> userEventManager;

    private List<String> limitedService = new ArrayList<String>();

    public UserServiceImpl() {
        limitedService.addAll(Arrays.asList(
                UserServiceName.DELETE.toString()
        ));
        if (LOGGER.isDebugEnabled()) LOGGER.debug("Limited Service: " + limitedService.toString());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<String> getLimitedService() {
        return limitedService;
    }

    public String execute(String convertedServiceName, User o) {
        String serviceResult = WebServiceResult.CODE_404.alias;
        UserServiceName userServiceName;
        try {
            userServiceName = UserServiceName.valueOf(convertedServiceName);
        } catch (IllegalArgumentException e) {
            return serviceResult;
        }
        switch (userServiceName) {
            case LOGIN:
                serviceResult = login(o);
                break;
            case REGISTER:
                serviceResult = register(o);
                break;
            case LOGOUT:
                serviceResult = logout(o);
                break;
            case CHECK_USERNAME:
                serviceResult = checkUsername(o);
                break;
            case CHECK_NEWPASS:
                serviceResult = checkNewpass(o);
                break;
            case CHECK_CONFPASS:
                serviceResult = checkConfpass(o);
                break;
            case CHECK_NICKNAME:
                serviceResult = checkNickname(o);
                break;
            case DELETE:
                serviceResult = delete(o);
                break;
            case INDEX:
                serviceResult = index(o);
                break;
        }
        return serviceResult;
    }

    private String index(User o) {
        list(o);
        return UserServiceResult.INDEX.alias;
    }

    public String list(User o) {
        if (o.getLimit() == null) o.setLimit(50);
        if (o.getOffset() == null) o.setOffset(0);
        o.setTotal(userDAO.count());
        o.setList(userDAO.page(o));
        return WebServiceResult.JSON.alias;
    }

    public String delete(User o) {
        if (o.username != null) {
            User toDelete = userDAO.getByUsername(o.username);
            if (toDelete != null) {
                userDAO.delete(toDelete);
            }
        }
        return WebServiceResult.JSON.alias;
    }

    /**
     * Method login ...
     * TODO: 3次登录失败则添加验证码校验
     *
     * @param o of type UserDTO
     * @return {@link UserServiceResult} to decide the view
     */
    public String login(User o) {
        if (getRequest().getMethod().equalsIgnoreCase("GET")) {
            return getLoginUser() == null ?
                    UserServiceResult.LOGIN.alias : WebServiceResult.REDIRECT_TO_ROOT.alias;
        }
        final String username = o.username;
        final String password = o.password;
        /**
         * 当用户名不合法时, 提示USERNAME_UNEXISTENT 而不是 USERNAME_INVALID
         * 因为数据库中肯定不会存在不合法的用户名，这样可以避免对数据库进行无谓的查询,
         * 注: 这种校验一般放在页面上用js执行
         * 下同
         */
        if (isInvalidUsername(username)) {
            o.addMessage(Message.USER_USERNAME_NONEXISTENT);
            return UserServiceResult.LOGIN.alias;
        }
        // TODO: 完善业务处理过程，提高性能，如尽可能减少数据库查询
        User user = userDAO.getByUsername(username);
        if (user == null) {
            o.addMessage(Message.USER_USERNAME_NONEXISTENT);
            return UserServiceResult.LOGIN.alias;
        }
        if (!user.password.equalsIgnoreCase(SecureUtils.encryptPassword(username, password))) {
            o.addMessage(Message.USER_PASSWORD_INCORRECT);
            return UserServiceResult.LOGIN.alias;
        }
        o.fillByModel(user);
        doLogin(o);
        if (Boolean.TRUE.equals(o.getAutologin())) {
            setLoginCookies(o);
        }
        userEventManager.fireAfterEvent(new UserEvent(o));
        return WebServiceResult.REDIRECT_TO_ROOT.alias;
    }

    // 经过登录验证, 或者激活, 或者重置密码后, 执行login操作
    private void doLogin(User o) {
        removeInactiveUser();
        o.lasthost = getRequest().getRemoteAddr();
        o.online = 1;
        o.lastlogin = new Timestamp(System.currentTimeMillis());
        userDAO.login(o);
        setLoginUser(o);
    }

    /**
     * Method logout ...
     *
     * @param o of type UserDTO
     * @return true if service succeed, otherwise false
     */
    @SuppressWarnings("unused")
    public String logout(User o) {
        User sessionUserDTO = getLoginUser();
        if (sessionUserDTO != null) {
            userDAO.logout(sessionUserDTO.id);
            removeLoginUser();
            clearLoginCookies();
        }
        return WebServiceResult.REDIRECT_TO_ROOT.alias;
    }

    /**
     * 创建一个新的用户, 并激活空间, 自动创建默认日志分类
     *
     * @param o user
     * @return true if service succeed, otherwise false
     */
    public String register(User o) {
        if (getRequest().getMethod().equalsIgnoreCase("GET")) {
            return UserServiceResult.REGISTER.alias;
        }

        //检验字段是否合法 o.username o.newpass o.confoass o.nickname
        checkUsername(o);
        checkNewpass(o);
        checkConfpass(o);
        checkNickname(o);

        if (o.hasMessages()) {
            o.addMessage(Message.REGISTER_ERROR);
            return UserServiceResult.REGISTER.alias;
        }

        o.password = SecureUtils.encryptPassword(o.username, o.getNewpass());
        o.lasthost = getRequest().getRemoteHost();
        User saveUser = o.toInsertModel();
        userDAO.save(saveUser);
        o.fillByModel(saveUser);
        //注册后自动登录,
        doLogin(o);
        userEventManager.fireAfterEvent(new UserEvent(o));
        return WebServiceResult.REDIRECT_TO_ROOT.alias;
    }

    /**
     * uri matches "/user/check-username?username=xxx"
     *
     * @param o of type UserDTO
     * @return "json"
     */
    public String checkUsername(final User o) {
        final String username = o.username;
        if (isInvalidUsername(username)) {
            o.addMessage("username", Message.USER_USERNAME_INVALID);
        } else if (hasUsername(username)) {
            o.addMessage("username", Message.USER_USERNAME_EXISTENT);
        }
        return WebServiceResult.JSON.alias;
    }

    public String checkNewpass(final User o) {
        if (isInvalidPassword(o.getNewpass())) {
            o.addMessage("newpass", Message.USER_NEWPASS_INVALID);
        }
        return WebServiceResult.JSON.alias;
    }

    public String checkConfpass(User o) {
        final String newpass = o.getNewpass();
        final String confpass = o.getConfpass();
        if (confpass == null || !confpass.equals(newpass)) {
            o.addMessage("confpass", Message.USER_CONFPASS_MISMATCH);
        }
        return WebServiceResult.JSON.alias;
    }

    public String checkNickname(final User o) {
        final String nickname = o.nickname;
        if (isInvalidNickname(nickname)) {
            o.addMessage("nickname", Message.USER_NICKNAME_INVALID);
        } else if (hasNickname(nickname)) {
            o.addMessage("nickname", Message.USER_NICKNAME_EXISTENT);
        }
        return WebServiceResult.JSON.alias;
    }

    /**
     * 用户名必须是有效的Email地址.
     *
     * @param username the name of User
     * @return True if username is illegal, otherwise false.
     */
    private boolean isInvalidUsername(String username) {
        return username == null || !Validator.USERNAME.matches(username);
    }

    /**
     * check if the password is invalid
     *
     * @param password user's password
     * @return false if password is null or password's length is not between 6 and 32, otherwise true
     */
    private boolean isInvalidPassword(String password) {
        return password == null || !(6 <= password.length() && password.length() <= 32);
    }

    /**
     * 昵称必须为4-32个字节(一个汉字占2个字节)
     *
     * @param nickname the nickname of User
     * @return True if nickname is illegal, otherwise false.
     */
    private boolean isInvalidNickname(String nickname) {
        if (nickname == null) return true;
        String v = nickname.trim();
        int len = 0;
        for (int ix = 0; ix < v.length(); ix++) {
            char ch = v.charAt(ix);
            len += ch <= 255 ? 1: 2;
        }
        return len < 4 || len > 32 || !Validator.NICKNAME.matches(v);
    }

    private boolean hasUsername(String username) {
        return userDAO.countByUsername(username) > 0;
    }

    private boolean hasNickname(String nickname) {
        return userDAO.countByNickname(nickname) > 0;
    }

    enum Validator {
        USERNAME("^\\w+([-+\\._]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"),
        NICKNAME("[-_\\.\\w\\u4e00-\\u9fa5]+"),;

        private Pattern p;

        Validator(String regex) {
            p = Pattern.compile(regex);
        }

        public boolean matches(CharSequence input) {
            return p.matcher(input).matches();
        }
    }
}
